﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Tom.Algorithm
{
    // Roulette Wheel selector
    class GeneticSelectorRW<T> : IGeneticSelector<T>
    {
        // Hard-coded parameters
        const double MIN = 0.8;
        const double MAX = 1.2;

        // Store the population
        protected List<GeneticIndividual<T>> _Pop;

        // RNG
        protected Random _Rnd;

        // Store the individual fitness and total values
        protected double _TotalFitness;
        protected List<double> _Fitness;

        // Default constructor
        public GeneticSelectorRW() : this(new Random()) { }

        // Constructor
        public GeneticSelectorRW(Random rnd)
        {
            _Rnd = rnd;
            _Fitness = new List<double>();
        }

        // Remember the assign population and calculate their fitnesses
        public void AssignPopulation(List<GeneticIndividual<T>> pop)
        {
            // link to the assigned population
            _Pop = pop;

            // Calculate the fitness
            _Fitness.Clear();
            _TotalFitness = 0.0;

            int popSize = pop.Count;
            for (int i = 0; i < popSize; i++)
            {
                _Fitness.Add(MIN + (MAX - MIN) * i / (popSize - 1));
                _TotalFitness += _Fitness[i];
            }
        }

        // Selection an individual for crossover
        public GeneticIndividual<T> SelectForCrossover()
        {
            if (_Pop == null) {
                throw new Exception("No population assigned");
            }

            double x = _Rnd.NextDouble() * _TotalFitness;

            int i = 0;
            for (i = 0; i < _Pop.Count; i++)
            {
                if (x < _Fitness[i])
                    break;
                else
                    x -= _Fitness[i];
            }

            return _Pop[i];
        }
    }
}        
